﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using Pittypat.Configuration;

namespace Pittypat
{
    /// <summary>
    /// 提供一个功能的元数据信息和配置信息。
    /// </summary>
    public sealed class FunctionMeta
    {
        // 表示一个默认的 FunctionAttribute，当没有在 FunctionArgs 类的继承类型上声明 FunctionAttribute 时使用此默认的 FunctionAttribute
        static readonly FunctionAttribute DefaultFunctionAttribute = new FunctionAttribute();

        /// <summary>
        /// 使用应用配置、模块配置、功能配置、功能元数据以及参数元数据信息列表初始化 FunctionMeta 类的新实例。
        /// </summary>
        /// <param name="appConfig">应用的配置信息。</param>
        /// <param name="moduleConfig">模块的配置信息。</param>
        /// <param name="config">功能的配置信息。</param>
        /// <param name="attr">功能的元数据信息。</param>
        /// <param name="parameters"></param>
        private FunctionMeta(AppConfig appConfig, ModuleConfig moduleConfig, FunctionConfig config, FunctionAttribute attr, ParameterMeta[] parameters)
        {
            this.AppConfig = appConfig;
            this.ModuleConfig = moduleConfig;
            this.Config = config;
            this.Attribute = attr ?? DefaultFunctionAttribute;
            this.Parameters = parameters;

            // 如果模块配置信息没有提供目标数据库架构名称，则使用默认的数据库架构名称，然后组合存储过程名称作为数据库访问时存储过程的完整名称。
            if (string.IsNullOrEmpty(moduleConfig.DataSchema))
            {
                this.SpQualifiedName = string.Format("{0}_{1}.p_{2}_{3}", appConfig.Developer, appConfig.Identifier, moduleConfig.Identifier, config.Identifier).ToLower();
            }
            else
            {
                this.SpQualifiedName = string.Format("{0}.p_{1}_{2}", moduleConfig.DataSchema, moduleConfig.Identifier, config.Identifier).ToLower();
            }
        }

        /// <summary>
        /// 获取目标应用的配置信息，来自配置文件。
        /// </summary>
        public AppConfig AppConfig { get; private set; }

        /// <summary>
        /// 获取目标模块的配置信息，来自配置文件。
        /// </summary>
        public ModuleConfig ModuleConfig { get; private set; }

        /// <summary>
        /// 获取目标功能的配置信息，来自配置文件。
        /// </summary>
        public FunctionConfig Config { get; private set; }

        /// <summary>
        /// 获取定义在功能上的属性元数据，来自功能参数列表实体类的声明。如果没有声明，则使用默认的元数据信息。
        /// </summary>
        public FunctionAttribute Attribute { get; private set; }

        /// <summary>
        /// 获取功能定义的参数列表，来自功能参数列表实体类的属性元数据信息。
        /// </summary>
        public ParameterMeta[] Parameters { get; private set; }

        /// <summary>
        /// 获取功能对应的存储过程的数据架构限定的名称。
        /// </summary>
        /// <remarks>
        /// 如果目标模块配置了 dataSchema，则存储过程限定名称为：{dataSchema}.p_{模块标识符}_{功能标识符}。
        /// 如果目标模块没有配置 dataSchema，则存储过程限定名称为：{开发者代码}_{应用标识符}.p_{模块标识符}_{功能标识符}。
        /// </remarks>
        public string SpQualifiedName { get; private set; }

        #region 缓存

        // 功能元数据的缓存，键为功能参数列表实体类的类型，值为 FunctionMeta 对象实例。
        static readonly Dictionary<Type, FunctionMeta> metas = new Dictionary<Type, FunctionMeta>(128);

        // 同步对 metas 缓存的访问
        static readonly object metasLock = new object();

        // 表示一个空的参数列表
        static readonly ParameterMeta[] EmptyParameters = new ParameterMeta[0];

        /// <summary>
        /// 获取指定 <see cref="FunctionArgs"/> 类型对应的功能配置信息和参数元数据列表。该方法会在内部缓存此前加载过的元数据，以便减少反射带来的性能损失。
        /// </summary>
        /// <param name="argsType">从 <see cref="FunctionArgs"/> 继承的类型，也就是功能参数列表实体类的类型。</param>
        /// <returns>一个 <see cref="FunctionMeta"/> 对象实例，表示目标类型对应功能的配置信息和参数信息。</returns>
        /// <exception cref="ArgumentNullException">argsType 为 null。</exception>
        /// <exception cref="ConfigurationErrorsException">没有找到功能的配置信息。</exception>
        /// <exception cref="ArgumentException">argsType 是一个抽象类型，或者无法从 argsType 命名空间中解析开发者代码和应用标识符。</exception>
        public static FunctionMeta Find(Type argsType)
        {
            if (argsType == null)
            {
                throw new ArgumentNullException("argsType");
            }

            // argsType 不是一个 FunctionArgs 的子类
            if (!argsType.IsSubclassOf(typeof(FunctionArgs)))
            {
                throw new ArgumentException(argsType.FullName + " 不是一个 FunctionArgs 类型。", "argsType");
            }

            // argsType 是一个抽象类型。
            if (argsType.IsAbstract)
            {
                throw new ArgumentException(argsType.FullName + " 是一个抽象类型。", "argsType");
            }

            lock (metasLock)
            {
                FunctionMeta meta = null;

                // 首先尝试从缓存中获取功能的 FunctionMeta 对象实例
                if (!metas.TryGetValue(argsType, out meta))
                {
                    // 缓存中不存在目标功能的 FunctionMeta
                    // 其他 argsType 的命名空间格式为：{开发者代码}.{应用标识符}.{模块标识符}.Args
                    var names = argsType.Namespace.Split('.');
                    if (names.Length < 3)
                    {
                        throw new ArgumentException("无法从 " + argsType.FullName + " 的命名空间解析开发者代码和应用标识符。");
                    }

                    // 根据开发者代码和应用标识符获取应用配置信息
                    var appConfig = PittypatConfig.Current.Apps[names[0], names[1]];

                    if (appConfig == null)
                    {
                        throw new ConfigurationErrorsException(string.Format("没有找到 App '{0}.{1}' 的配置信息。", names[0], names[1]));
                    }

                    // 根据模块标识符获取模块配置信息
                    var moduleConfig = appConfig.Modules[names[2]];

                    if (moduleConfig == null)
                    {
                        throw new ConfigurationErrorsException(string.Format("没有找到模块 '{0}' 的配置信息。", argsType.Namespace));
                    }

                    // 根据 argsType 类型名称，也就是功能标识符获取功能的配置信息
                    var funcConfig = moduleConfig.Functions[argsType.Name];
                    if (funcConfig == null)
                    {
                        throw new ConfigurationErrorsException(string.Format("没有找到功能 '{0}.{1}.{2}.{3}' 的配置信息。", names[0], names[1], names[2], argsType.Name));
                    }

                    // 通过反射获取功能的参数属性列表
                    ParameterMeta[] parameters = null;
                    var props = argsType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                    if (props.Length > 0)
                    {
                        var list = new List<ParameterMeta>(props.Length);
                        ParameterAttribute attr = null;

                        // 查找参数的 ParameterAttribute，并创建每一个参数的 ParameterMeta 对象实例
                        foreach (var prop in props)
                        {
                            attr = prop.GetCustomAttribute<ParameterAttribute>();
                            if (attr != null)
                            {
                                list.Add(new ParameterMeta(attr, prop));
                            }
                        }

                        if (list.Count > 0)
                        {
                            // 对参数列表按参数位置进行排序，并添加到数组中
                            if (list.Count == 1)
                            {
                                // 仅有一个参数，不需要排序
                                parameters = new ParameterMeta[1];
                                parameters[0] = list[0];
                            }
                            else
                            {
                                // 按参数位置排序，位置越小越靠前
                                list.Sort((x, y) => x.Position - y.Position);
                                parameters = list.ToArray();
                            }
                        }
                    }

                    // 创建新的 FunctionMeta 对象实例并缓存到字典
                    meta = new FunctionMeta(appConfig, moduleConfig, funcConfig, argsType.GetCustomAttribute<FunctionAttribute>(), parameters ?? EmptyParameters);
                    metas.Add(argsType, meta);
                }

                return meta;
            }
        }

        #endregion
    }
}
